package com.cygsunri.consumption.api;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.cygsunri.common.service.CommonDataService;
import com.cygsunri.consumption.entity.*;
import com.cygsunri.consumption.entity.enumerate.EnergySort;
import com.cygsunri.consumption.service.*;
import com.cygsunri.measurement.entity.Flag;
import com.cygsunri.measurement.entity.MeasurementValue;
import com.cygsunri.rbac.entity.Account;
import com.cygsunri.rbac.service.AccountService;
import com.cygsunri.util.DateUtil;
import com.cygsunri.util.TimeUtil;
import com.github.abel533.echarts.Option;
import com.github.abel533.echarts.axis.CategoryAxis;
import com.github.abel533.echarts.series.Bar;
import com.github.abel533.echarts.series.Line;
import com.github.abel533.echarts.series.Pie;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.List;

/**
 * 能耗API
 */
@RestController
@RequestMapping("/api/consumption")
public class ConsumptionController {

    @Autowired
    private BuildingService buildingService;

    @Autowired
    private BranchService branchService;

    @Autowired
    private DepartmentService departmentService;

    @Autowired
    private AreaService areaService;

    @Autowired
    private EnergyItemService energyItemService;

    @Autowired
    private AccountService accountService;

    @Autowired
    private ConsumptionService consumptionService;

    @Autowired
    private CommonDataService commonDataService;

    @Autowired
    private QuotaService quotaService;

    /**
     * 获取所有建筑
     */
    @RequestMapping(value = "/building/all", method = RequestMethod.GET)
    public ResponseEntity<List<Building>> getBuilding() {
        return new ResponseEntity<>(buildingService.getAllBuilding(), HttpStatus.OK);
    }

    /**
     * 保存建筑
     */
    @RequestMapping(value = "/building/save", method = RequestMethod.POST)
    public ResponseEntity saveBuilding(Building building) {
        buildingService.saveBuilding(building);
        return new ResponseEntity<>(HttpStatus.OK);
    }

    /**
     * 根据用户获得建筑
     * @param users 用户name
     */
    @RequestMapping(value = "/all/buildings/users/{users}", method = RequestMethod.GET)
    public ResponseEntity<List<BuildingSort>> getBuildingSorts(@PathVariable("users") String users) {
        List<BuildingAccountMapping> buildingAccountMappings = buildingService.getBuildingMappingByAccount(users);
        return new ResponseEntity<>(buildingService.getBuildingSorts(buildingAccountMappings), HttpStatus.OK);
    }

    /**
     * 根据建筑获得能源分类
     * @param buildingID 建筑ID
     */
    @RequestMapping(value = "/energy/sorts/buildings/{buildingID}", method = RequestMethod.GET)
    public ResponseEntity<List<EnergySort>> getEnergySorts(@PathVariable("buildingID") String buildingID) {
        return new ResponseEntity<>(buildingService.getBuildingEnergySort(buildingID), HttpStatus.OK);
    }

    /**
     * 根据建筑和能源类型获得支路，并按照层次排列
     * @param buildingId 建筑ID
     * @param energySort 能耗类型
     */
    @RequestMapping(value = "/energy/branches/buildings/{buildingId}/energy/sorts/{energySort}", method = RequestMethod.GET)
    public ResponseEntity<List<Branch>> getBranches(@PathVariable("buildingId") String buildingId, @PathVariable("energySort") String energySort) {
        List<Branch> branches = branchService.getBranchesByEnergySortWithOutBuilding(energySort);
        branches.addAll(branchService.getBranchesByBuildingAndEnergySort(buildingId, energySort));
        return new ResponseEntity<>(branchService.sortBranches(branches), HttpStatus.OK);
    }

    /**
     * 根据建筑和能源类型获得部门，并按照层次排列
     * @param buildingId 建筑ID
     * @param energySort 能耗类型
     */
    @RequestMapping(value = "/departments/buildings/{buildingId}/energy/sorts/{energySort}", method = RequestMethod.GET)
    public ResponseEntity<List<Department>> getDepartments(@PathVariable("buildingId") String buildingId, @PathVariable("energySort") String energySort) {
        List<Department> departments = departmentService.getDepartmentsByBuildingAndEnergySort(buildingId, energySort);
        return new ResponseEntity<>(departmentService.sortDepartments(departments), HttpStatus.OK);
    }

    /**
     * 根据建筑和能源类型获得区域，并按照层次排列
     * @param buildingId 建筑ID
     * @param energySort 能耗类型
     */
    @RequestMapping(value = "/areas/buildings/{buildingId}/energy/sorts/{energySort}", method = RequestMethod.GET)
    public ResponseEntity<List<Area>> getAreas(@PathVariable("buildingId") String buildingId, @PathVariable("energySort") String energySort) {
        List<Area> areas = areaService.getAreasByBuildingAndEnergySort(buildingId, energySort);
        return new ResponseEntity<>(areaService.sortAreas(areas), HttpStatus.OK);
    }

    /**
     * 根据建筑和能源类型获得分项，并按照层次排列
     * @param buildingId 建筑ID
     * @param energySort 能耗类型
     */
    @RequestMapping(value = "/energy/items/buildings/{buildingId}/energy/sorts/{energySort}", method = RequestMethod.GET)
    public ResponseEntity<List<EnergyItem>> getEnergyItems(@PathVariable("buildingId") String buildingId, @PathVariable("energySort") String energySort) {
        List<EnergyItem> energyItems = energyItemService.getEnergyItemByBuildingAndEnergySort(buildingId, energySort);
        return new ResponseEntity<>(energyItemService.sortEnergyItems(energyItems), HttpStatus.OK);
    }

    /**
     * 根据用户获取所有能耗类型统计（暂时写死）
     */
    @RequestMapping(value = "/all/statistic/category/users/{users}", method = RequestMethod.GET)
    public ResponseEntity<String> getEnergyItems(@PathVariable("users") String users) {
        JSONArray jsonArray = new JSONArray();
        jsonArray.add(new HashMap<String, Object>() {{
            put("name", "building");
            put("desc", "建筑用能");
        }});
        jsonArray.add(new HashMap<String, Object>() {{
            put("name", "branch");
            put("desc", "支路用能");
        }});
        jsonArray.add(new HashMap<String, Object>() {{
            put("name", "item");
            put("desc", "分项用能");
        }});
        return new ResponseEntity<>(JSONArray.toJSONString(jsonArray), HttpStatus.OK);
    }

    /**
     * 能耗统计
     * 日月年能耗、标准煤折算和同比环比、日月年节能和标准煤折算
     * @param user 用户name
     */
    @RequestMapping(value = "/gross/energy/users/{user}", method = RequestMethod.GET)
    public ResponseEntity<String> getAllConversionEnergy(@PathVariable("user") String user) {
        JSONArray jsonArray = new JSONArray();
        Account account = accountService.getAccountByName(user);
        List<EnergySort> energySorts = buildingService.getAccountEnergySort(account.getId());
        for (EnergySort energySort : energySorts) {
            JSONObject object = consumptionService.getAllConversionEnergy(account, energySort);
            jsonArray.add(object);
        }
        return new ResponseEntity<>(JSONArray.toJSONString(jsonArray), HttpStatus.OK);
    }

    /**
     * 能耗统计
     * 近30日能耗趋势和环比
     * @param user 用户name
     * @param energySort 能耗类型
     */
    @RequestMapping(value = "/past/few/days/energy/users/{user}/sorts/{energySort}", method = RequestMethod.GET)
    public ResponseEntity<String> getGeneralInformation(@PathVariable("user") String user, @PathVariable("energySort") String energySort) {
        Option option = new Option();
        CategoryAxis categoryAxis = new CategoryAxis();
        option.xAxis(categoryAxis);

        Bar bar = new Bar("总能耗");
        option.series(bar);

        Line dayToDay = new Line("环比");
        option.series(dayToDay);

        Account account = accountService.getAccountByName(user);
        List<String> days = DateUtil.getDays(DateUtil.today(), true);
        int i = 0;
        for (String day : days) {
            categoryAxis.data(day);
            MeasurementValue m = commonDataService.getValue(account.getPsrID(), energySort, TimeUtil.toMilliSeconds(day), Flag.DAY_DIFF);
            bar.data(m.isInValid() ? "-" : m.getData());

            Double dayToDayValue;
            if (i == 0) {
                MeasurementValue beforeDay = commonDataService.getValue(account.getPsrID(), energySort, TimeUtil.toMilliSeconds(DateUtil.beforeDay(day)), Flag.DAY_DIFF);
                dayToDayValue = (m.isInValid() || beforeDay.isInValid() || beforeDay.getData() == 0d) ? Double.NaN : (m.getData() - beforeDay.getData()) / beforeDay.getData() * 100;
            } else {
                Double beforeDay = bar.data().get(i - 1).equals("-") ? Double.NaN : (Double) bar.data().get(i - 1);
                dayToDayValue = (m.isInValid() || beforeDay.isNaN() || beforeDay == 0d) ? Double.NaN : (m.getData() - beforeDay) / beforeDay * 100;
            }
            dayToDay.data(dayToDayValue.isNaN() ? "-" : dayToDayValue);
            i++;
        }
        return new ResponseEntity<>(JSON.toJSONString(option), HttpStatus.OK);
    }

    /**
     * 能耗统计
     * 整点实时能耗
     * @param user 用户name
     * @param energySort 能耗类型
     */
    @RequestMapping(value = "/realtime/energy/users/{user}/sorts/{energySort}/steps/{step}", method = RequestMethod.GET)
    public ResponseEntity<String> getRealTimeEnergyConsumption(@PathVariable("user") String user, @PathVariable("energySort") String energySort, @PathVariable("step") Integer step) {
        Option option = new Option();
        CategoryAxis categoryAxis = new CategoryAxis();
        option.xAxis(categoryAxis);

        Line line = new Line("实时能耗");
        option.series(line);
        List<String> times = DateUtil.getHours();
        times.remove(0);
        Account account = accountService.getAccountByName(user);
        for (String time : times) {
            categoryAxis.data(time.substring(0, 5));
            MeasurementValue m;
            if ("electricity".equals(energySort.toLowerCase())) {
                m = commonDataService.getValue(account.getPsrID(), energySort, TimeUtil.toMilliSeconds(DateUtil.today() + " " + time), Flag.HOUR_DIFF);
            } else {
                m = commonDataService.getHistoryValue("Cep", "Pressure", DateUtil.today() + " " + time);
            }
            line.data(m.isInValid() ? "-" : m.getData());
        }
        return new ResponseEntity<>(JSON.toJSONString(option), HttpStatus.OK);
    }

    /**
     * 能耗对比
     * 用电日月年能耗-按类型，昨日上月去年同期
     * @param user 用户name
     * @param energySort 能耗类型
     * @param dateType 时间类型
     * @param date 时间
     */
    @RequestMapping(value = "/energy/stack/bar/by/items/users/{user}/sorts/{energySort}/dateType/{dateType}/dates/{date}", method = RequestMethod.GET)
    public ResponseEntity<String> getEnergyConsumptionStackBarByItems(@PathVariable("user") String user,
                                                                      @PathVariable("energySort") String energySort,
                                                                      @PathVariable("dateType") String dateType,
                                                                      @PathVariable("date") String date) {
        Account account = accountService.getAccountByName(user);
        List<EnergyItem> energyItems = energyItemService.getEnergyItemByAccountAndEnergySort(account.getId(), energySort);

        Option option = new Option();
        CategoryAxis categoryAxis = new CategoryAxis();
        option.xAxis(categoryAxis);

        for (EnergyItem energyItem : energyItems) {
            Bar bar = new Bar(energyItem.getName());
            bar.setStack("分类能耗");
            option.series(bar);
        }

        Bar otherLine = new Bar("其他");
        otherLine.setStack("分类能耗");
        option.series(otherLine);

        Line totalLine = new Line("总能耗");
        option.series(totalLine);

        Line beforeTotalLine = new Line();
        option.series(beforeTotalLine);

        List<String> times;
        int flag;
        switch (dateType) {
            case "day":
                beforeTotalLine.name("昨日同期");
                times = DateUtil.getHours(date);
                times.remove(0);
                flag = Flag.HOUR_DIFF;
                option = consumptionService.handleEnergyItemOption(option, account, energyItems, energySort, times, flag);
                break;
            case "month":
                beforeTotalLine.name("上月同期");
                times = DateUtil.getDays(date);
                flag = Flag.DAY_DIFF;
                option = consumptionService.handleEnergyItemOption(option, account, energyItems, energySort, times, flag);
                break;
            case "year":
                beforeTotalLine.name("去年同期");
                times = DateUtil.getMonths(date);
                flag = Flag.MONTH_DIFF;
                option = consumptionService.handleEnergyItemOption(option, account, energyItems, energySort, times, flag);
                break;
            default:
                return new ResponseEntity<>(HttpStatus.NOT_FOUND);
        }
        return new ResponseEntity<>(JSON.toJSONString(option), HttpStatus.OK);
    }

    /**
     * 能耗对比
     * 用电用水日月年能耗-按建筑，昨日上月去年同期
     * @param user 用户name
     * @param energySort 能耗类型
     * @param dateType 时间类型
     * @param date 时间
     */
    @RequestMapping(value = "/energy/stack/bar/by/buildings/users/{user}/sorts/{energySort}/dateType/{dateType}/dates/{date}", method = RequestMethod.GET)
    public ResponseEntity<String> getEnergyConsumptionStackBarByBuildings(@PathVariable("user") String user,
                                                                          @PathVariable("energySort") String energySort,
                                                                          @PathVariable("dateType") String dateType,
                                                                          @PathVariable("date") String date) {
        Account account = accountService.getAccountByName(user);
        List<Building> buildings = buildingService.getBuildingByAccount(account.getId());

        Option option = new Option();
        CategoryAxis categoryAxis = new CategoryAxis();
        option.xAxis(categoryAxis);
        for (Building building : buildings) {
            Bar bar = new Bar(building.getName());
            bar.setStack("建筑能耗");
            option.series(bar);
        }

        Bar otherLine = new Bar("其他");
        otherLine.setStack("建筑能耗");
        option.series(otherLine);

        Line totalLine = new Line("总能耗");
        option.series(totalLine);

        Line beforeTotalLine = new Line();
        option.series(beforeTotalLine);

        List<String> times;
        int flag;
        switch (dateType) {
            case "day":
                beforeTotalLine.name("昨日同期");
                times = DateUtil.getHours(date);
                times.remove(0);
                flag = Flag.HOUR_DIFF;
                option = consumptionService.handleBuildingOption(option, account, buildings, energySort, times, flag);
                break;
            case "month":
                beforeTotalLine.name("上月同期");
                times = DateUtil.getDays(date);
                flag = Flag.DAY_DIFF;
                option = consumptionService.handleBuildingOption(option, account, buildings, energySort, times, flag);
                break;
            case "year":
                beforeTotalLine.name("去年同期");
                times = DateUtil.getMonths(date);
                flag = Flag.MONTH_DIFF;
                option = consumptionService.handleBuildingOption(option, account, buildings, energySort, times, flag);
                break;
            default:
                return new ResponseEntity<>(HttpStatus.NOT_FOUND);
        }
        return new ResponseEntity<>(JSON.toJSONString(option), HttpStatus.OK);
    }

    /**
     * 能耗对比
     * 月区域耗能总量和升降排行
     * @param user 用户name
     * @param energySort 能耗类型
     * @param type 耗能总量/升降比率
     */
    @RequestMapping(value = "/month/gross/users/{user}/sorts/{energySort}/types/{type}", method = RequestMethod.GET)
    public ResponseEntity<String> getBuildingEnergyConsumptionRankingByEnergySort(@PathVariable("user") String user, @PathVariable("energySort") String energySort, @PathVariable("type") String type) {
        Account account = accountService.getAccountByName(user);
        List<Building> buildings = buildingService.getBuildingByAccount(account.getId());
        Option option = new Option();
        Pie pie = new Pie(type.equals("gross") ? "耗能总量" : "升降比率");
        option.series(pie);

        for (Building building : buildings) {
            MeasurementValue m = commonDataService.getValue(building.getPsrID(), energySort, DateUtil.nowMilliSeconds(), Flag.MONTH_DIFF);
            if (type.equals("gross")) {
                pie.data(new HashMap<String, Object>() {{
                    put("name", building.getName());
                    put("value", m.isInValid() ? "-" : m.getData());
                }});
            } else {
                MeasurementValue beforeM = commonDataService.getValue(building.getPsrID(), energySort, DateUtil.lastMonth(), Flag.MONTH_DIFF);
                pie.data(new HashMap<String, Object>() {{
                    put("name", building.getName());
                    put("value", m.isInValid() || beforeM.isInValid() || beforeM.getData() == 0d ? "-" : (m.getData() - beforeM.getData()) / beforeM.getData());
                }});
            }
        }
        return new ResponseEntity<>(JSON.toJSONString(option), HttpStatus.OK);
    }

    /**
     * 能耗对比
     * 能耗定额执行
     * @param user 用户name
     */
    @RequestMapping(value = "/building/quota/users/{user}", method = RequestMethod.GET)
    public ResponseEntity<String> getConsumptionQuota(@PathVariable("user") String user) {
        JSONArray jsonArray = new JSONArray();
        Account account = accountService.getAccountByName(user);
        List<EnergySort> energySorts = buildingService.getAccountEnergySort(account.getId());
        String date = DateUtil.today();
        for (EnergySort energySort : energySorts) {
            JSONObject object = new JSONObject(true);
            object.put("energySort", energySort.getName());

            Quota monthQ = quotaService.getQuotaByEnergySortAndType(date, energySort.getName(), "month");
            MeasurementValue month = commonDataService.getValue(account.getPsrID(), energySort.getName(), TimeUtil.toMilliSeconds(LocalDateTime.now()), Flag.MONTH_DIFF);
            object.put("monthQuota", monthQ == null ? "-" : monthQ.getQuota_value());
            object.put("monthConsume", month.isInValid() ? "-" : month.getData());
            object.put("monthRemain", month.isInValid() || monthQ == null ? "-" : monthQ.getQuota_value() - month.getData());
            object.put("monthRatio", month.isInValid() || monthQ == null || monthQ.getQuota_value() == 0d ? "-" : month.getData() / monthQ.getQuota_value());

            Quota yearQ = quotaService.getQuotaByEnergySortAndType(date, energySort.getName(), "year");
            MeasurementValue year = commonDataService.getValue(account.getPsrID(), energySort.getName(), TimeUtil.toMilliSeconds(LocalDateTime.now()), Flag.YEAR_DIFF);
            object.put("yearQuota", yearQ == null ? "-" : yearQ.getQuota_value());
            object.put("yearConsume", year.isInValid() ? "-" : year.getData());
            object.put("yearRemain", year.isInValid() || yearQ == null ? "-" : yearQ.getQuota_value() - year.getData());
            object.put("yearRatio", year.isInValid() || yearQ == null || yearQ.getQuota_value() == 0d ? "-" : year.getData() / yearQ.getQuota_value());

            jsonArray.add(object);
        }
        return new ResponseEntity<>(JSONArray.toJSONString(jsonArray), HttpStatus.OK);
    }

    /**
     * 能耗对比
     * 用电用水饼图
     * @param user 用户name
     * @param energySort 能耗类型
     * @param dateType 时间类型
     * @param date 时间
     */
    @RequestMapping(value = "/energy/pie/users/{user}/sorts/{energySort}/dateType/{dateType}/dates/{date}", method = RequestMethod.GET)
    public ResponseEntity<String> getEnergyConsumptionPie(@PathVariable("user") String user,
                                                          @PathVariable("energySort") String energySort,
                                                          @PathVariable("dateType") String dateType,
                                                          @PathVariable("date") String date) {
        Account account = accountService.getAccountByName(user);
        List<EnergyItem> energyItems = energyItemService.getEnergyItemByAccountAndEnergySort(account.getId(), energySort);
        Option option = new Option();
        Pie pie = new Pie();
        option.series(pie);
        int flag;
        switch (dateType) {
            case "day":
                flag = Flag.DAY_DIFF;
                break;
            case "month":
                flag = Flag.MONTH_DIFF;
                break;
            case "year":
                flag = Flag.YEAR_DIFF;
                break;
            default:
                return new ResponseEntity<>(HttpStatus.NOT_FOUND);
        }

        Double itemTotal = 0d;
        for (EnergyItem energyItem : energyItems) {
            List<Branch> branches = branchService.getBranchesByAccountAndEnergyItem(account.getId(), energyItem.getId());
            Double sum = 0d;
            for (Branch branch : branches) {
                MeasurementValue m = commonDataService.getValue(branch.getPsrID(), energySort, TimeUtil.toMilliSeconds(date), flag);
                sum += m.isInValid() ? 0d : m.getData();
            }
            Double finalSum = sum;
            pie.data(new HashMap<String, Object>() {{
                put("name", energyItem.getName());
                put("value", finalSum);
            }});
            itemTotal += sum;
        }
        MeasurementValue total = commonDataService.getValue(account.getPsrID(), energySort, TimeUtil.toMilliSeconds(date), flag);
        Double other = total.isInValid() ? 0d : total.getData() - itemTotal;
        pie.data(new HashMap<String, Object>() {{
            put("name", "其他");
            put("value", other);
        }});
        return new ResponseEntity<>(JSONArray.toJSONString(option), HttpStatus.OK);
    }

}